import logging

from flask_admin.contrib.fileadmin import FileAdmin
from wtforms.validators import data_required as required, ValidationError
from app.core import CommonView
from app import db
from flask_login import current_user
from config import upload_path

logger = logging.getLogger()


class FileUploadView(FileAdmin):
    """继承 FileAdmin 类，实现自定义文件上传功能！"""
    can_mkdir = False
    can_delete_dirs = False

    def __init__(self, base_path, *args, **kwargs):
        super(FileUploadView, self).__init__(base_path, *args, **kwargs)

    def is_accessible(self):
        """当用户具有模块/菜单权限时，才允许访问"""
        curr_model_name = self.__class__.__name__
        if current_user.is_anonymous:
            return False
        else:
            res = current_user.get_resources()
            # print(res, curr_model_name)
            if res is not None and type(res) is list and curr_model_name in res:
                return True

        return False

    def get_resource_name(self):
        """获取类名，作为资源名称，添加资源列表中，用于用户菜单权限控制"""
        return self.__class__.__name__

    def get_breadcrumbs(self):
        breadcrumbs = []
        # 最多取三级菜单
        if self.category is not None:
            l2_menu = self.admin.get_category_menu_item(self.category)
            if l2_menu is not None:
                l3_menu = l2_menu.parent
                if l3_menu is not None and hasattr(l3_menu, "name"):
                    breadcrumbs.append(("", l3_menu.name))
            breadcrumbs.append(("", l2_menu.name))
        breadcrumbs.append(("", self.name))
        return breadcrumbs


# 自定义校验器，判断用户输入文件名是否正确、文件是否存在等
def file_validator(form, field):
    file_name = field.data
    file_name_arr = file_name.split('.')
    if len(file_name_arr) > 1:
        file_type = file_name_arr[-1].lower()
        if file_type not in ['xlsx', 'xls']:
            raise ValidationError(u'文件名类型后缀错误，目前只支持excel类型的文件！')
        else:
            import os
            file_path = upload_path  # os.path.join(os.path.dirname(os.path.dirname(__file__)), 'static/uploads')
            file_name_full = os.path.join(file_path, file_name)
            if not os.path.exists(file_name_full):
                raise ValidationError(u'文件不存在，请检查文件是否已上传、文件名是否正确！')
    else:
        raise ValidationError(u'文件名错误！')


async def deal_xls_to_db(model):
    logger.info('in deal_xls_to_db')
    # print('in deal_xls_to_db')
    file_name = model.file_name
    proc_type = model.proc_type
    if_del = model.if_del
    db_str = model.db_str
    db_type = model.db_type
    config_json = model.file_config_json
    # try:
    #     xdi = XlsDataImporter(file_name, proc_type, if_del, db_str, db_type, config_json)
    #     result = xdi.load()
    #     if result is not None:
    #         model.proc_result = result
    #         model.proc_state = '2'
    #         # logger.info('data file loaded!')
    #         # assume form has a field called remarks
    #         # model.remarks = form.remarks.data
    #         db.session.commit()
    # except SQLAlchemyError as se:
    #     logger.error('db error! %s', str(se))
    #     db.session.rollback()
    #     model.proc_result = '执行失败：' + str(se)
    #     model.proc_state = '3'
    #     db.session.commit()
    # except Exception as ex:
    #     logger.error('error! %s', str(ex))
    #     db.session.rollback()
    #     model.proc_result = 'err! ' + str(ex)
    #     model.proc_state = '3'
    #     db.session.commit()


class FileProcessView(CommonView):
    column_display_actions = True
    can_delete = True
    can_edit = False
    can_create = True
    column_searchable_list = ['file_name']
    column_list = ('id', 'file_name', 'proc_type', 'if_del', 'proc_state', 'proc_result', 'created_at')
    column_labels = {
        'file_name': '数据文件名',
        'proc_type': '处理类型',
        'if_del': '是否删除',
        'proc_state': '处理状态',
        'proc_rows': '数据条数',
        'proc_result': '处理结果',
        'created_at': '创建时间',
        'db_str': 'DB连接字符串',
        'db_type': 'DB类型',
        'file_config_json': '自定义表单格式',
    }
    column_sortable_list = ('id', 'file_name', 'proc_rows', 'created_at')
    column_default_sort = ('id', True)
    column_filters = ('proc_type', 'proc_state')
    column_choices = {
        'if_del': [
            ('1', '是'),
            ('0', '否')
        ],
        'proc_type': [
            ('0', '自定义数据文件导入'),
            ('1', '批次数据导入'),
            ('2', '产品信息导入'),
        ],
        'proc_state': [
            ('0', '未处理'),
            ('1', '处理中'),
            ('2', '已完成'),
            ('3', '处理失败')
        ],
        'db_type': [
            ('ORACLE', 'ORACLE'),
        ],
    }
    page_size = 20
    form_columns = ('file_name', 'proc_type', 'if_del', 'proc_state', 'proc_rows', 'proc_result', 'db_str', 'db_type',
                    'file_config_json')
    form_choices = column_choices

    form_args = {
        'file_name': {
            'validators': [required(), file_validator],
            'render_kw': {
                'placeholder': '请输入文件名：???.xlsx'
            }
        },
        'proc_type': {
            'validators': [required()],
        },
        'proc_state': {
            'validators': [required()],
        },
        'file_config_json': {
            'validators': [],
            'render_kw': {
                'placeholder': '请输入json格式的自定义表格数据描述信息'
            }
        },
    }

    form_widget_args = {
        'proc_rows': {
            'disabled': True
        },
        'proc_result': {
            'disabled': True,
        },
        'proc_state': {
            'disabled': True
        },
    }

    # def on_form_prefill(self, form, id):
    #     form.proc_state.render_kw = {'selected': True}
    def on_model_change(self, form, model, is_created):
        from app.utils import is_blank
        if model.proc_type == '0' and is_blank(model.file_config_json):
            raise ValidationError('当为自定义处理任务时，必须填写自定义表单格式!')

    def after_model_change(self, form, model, is_created):
        """当完成创建后，执行文件解析和导数入库操作！"""
        if is_created:
            logger.info('data file loading...')
            if hasattr(model, 'file_name') and hasattr(model, 'proc_type') and hasattr(model, 'if_del'):
                model.proc_state = '1'  # processing
                db.session.commit()
                # 线程池方式做异步任务，在本处不适用！SQLite是单线程，无法在新线程中访问和使用！
                # thread_pool_executor.submit(deal_xls_to_db, model)
                # 改为协程异步任务方式，且使用主线程的 EventLoop
                # load_xls_to_db = deal_xls_to_db(model)
                # event_loop.run_until_complete(load_xls_to_db)


class RptGenerateView(CommonView):
    column_display_actions = True
    can_delete = True
    can_edit = False
    can_create = True
    column_searchable_list = ['rpt_type']
    column_list = ('id', 'rpt_type', 'proc_state', 'created_at')
    column_labels = {
        'rpt_type': '报表数据类型',
        'proc_state': '处理状态',
        'proc_result': '处理结果',
        'created_at': '创建时间'
    }
    column_sortable_list = ('id', 'rpt_type', 'created_at')
    column_default_sort = ('id', True)
    column_filters = ('rpt_type',)
    column_choices = {
        'rpt_type': [
            ('1', '报表1数据生成'),
            ('2', '报表2数据生成'),
            ('3', '报表3数据生成'),
        ],
        'proc_state': [
            ('0', '未处理'),
            ('1', '处理中'),
            ('2', '已完成'),
            ('3', '处理失败')
        ],
    }
    page_size = 20
    form_columns = ('rpt_type', 'proc_state', 'proc_result')
    form_choices = column_choices

    form_args = {
        'rpt_type': {
            'validators': [required()],
        },
    }

    form_widget_args = {
        'proc_state': {
            'disabled': True
        },
        'proc_result': {
            'disabled': True,
        },
    }

    def after_model_change(self, form, model, is_created):
        """当完成创建后，执行报表数据SQL批处理！"""
        if is_created:
            if hasattr(model, 'rpt_type'):
                rpt_type = model.rpt_type
                # rdl = RptDataLoader(rpt_type)
                # result = rdl.load()
                # if result is not None:
                #     model.proc_state = '2'  # 已完成
                #     model.proc_result = result
                #     logger.info('report date generated!')
                #     db.session.commit()
